package math;

import java.awt.geom.Point2D;
import java.io.Serializable;

/**
 *
 * @author Luan
 */
public class Point {
    public float x, y;

    public Point() {
        this(0, 0);
    }

    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public Point(Point2D p) {
        this.x = (float) p.getX();
        this.y = (float) p.getY();
    }

    public void clear() {
        this.x = this.y = 0;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public void mover(float dx, float dy) {
        this.x += dx;
        this.y += dy;
    }

    public float dist(Point p) {
        return (float) Math.sqrt(Math.pow(this.x - p.x, 2) + Math.pow(this.y - p.y, 2));
    }
    
    public static float dist(float x1, float y1, float x2, float y2) {
        return (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    public static Point rotacionarRad(Point p, float angulo) {
        return rotacionarRad(p, angulo, 0, 0);
    }

    public static Point rotacionarRad(Point p, float angulo, Point centro) {
        return rotacionarRad(p, angulo, centro.x, centro.y);
    }

    public static Point rotacionarRad(Point p, float angulo, float x, float y) {
        float ax = p.x - x, ay = p.y - y;
        float nx = (float) (Math.cos(angulo) * ax + Math.sin(angulo) * ay);
        float ny = (float) (Math.cos(angulo) * ay - Math.sin(angulo) * ax);
        //Voltar à posição anterior
        nx += x;
        ny += y;
        return new Point(nx, ny);
    }

    public static Point rotacionar(Point p, float angulo) {
        return rotacionar(p, angulo, 0, 0);
    }

    public static Point rotacionar(Point p, float angulo, Point centro) {
        return rotacionar(p, angulo, centro.x, centro.y);
    }

    public static Point rotacionar(Point p, float angulo, float x, float y) {
        return rotacionarRad(p, (float) Math.toRadians(angulo), x, y);
    }

    public Point pontoMedio(Point p) {
        return Point.pontoMedio(this, p);
    }

    public float slope(Point p) {
        return (this.y - p.y)/(this.x - p.x);
    }

    //Divide o segmento desse ponto até fim em n partes, retornando os pts internos
    public Point[] dividirEm(Point fim, int qtd) {
        Point[] pts = new Point[qtd - 1];
        pts[0] = new Point(x + (fim.x - x)/qtd, y + (fim.y - y)/qtd);

        float dist = dist(pts[0]);
        Line r = new Line(this, fim);
        for (int i = 1; i < pts.length; i++)
            pts[i] = Line.bordaDe(r.whereDist(this, dist*(i + 1)), this, pts[0]);

        return pts;
    }

    public static Point pontoMedio(Point p1, Point p2) {
        return new Point((p1.x + p2.x)/2, (p1.y + p2.y)/2);
    }

    public Point pontoEm(float dx, float dy) {
        return new Point(x + dx, y + dy);
    }

    //Retorna a reta em que todos os pontos são equidistantes de p1 e p2
    public static Line getRetaEquidistante(Point p1, Point p2) {
        return new Line(-1/new Line(p1, p2).getM(), p1.pontoMedio(p2));
    }

    public static boolean entre(Point p1, Point p2, Point teste) {
        return ((teste.x >= p1.x && teste.x <= p2.x) || (teste.x <= p1.x && teste.x >= p2.x))
                && ((teste.y >= p1.y && teste.y <= p2.y) || (teste.y <= p1.y && teste.y >= p2.y));
    }

    public Point oposto() {
        return new Point(-x, -y);
    }

    public Point inverso() {
        return new Point(y, x);
    }

    public static String toString(float d) {
        if (Math.abs(d - Math.round(d)) < 0.0001)
            return String.valueOf((int) d);
        return String.valueOf(d);
    }

    @Override
    public String toString() {
        return "(" + toString(x) + ", " + toString(y) + ")";
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Point)
            return x == ((Point) o).x && y == ((Point) o).y;
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + Float.floatToIntBits(this.x) ^ (Float.floatToIntBits(this.x) >>> 32);
        hash = 37 * hash + Float.floatToIntBits(this.y) ^ (Float.floatToIntBits(this.y) >>> 32);
        return hash;
    }

    public static class Inteiro implements Serializable {

        private static final long serialVersionUID = 3898328033727920560L;
        public int x, y;

        public Inteiro() {
            this(0, 0);
        }

        public Inteiro(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        public void mover(int dx, int dy) {
            this.x += dx;
            this.y += dy;
        }

        public static math.Point.Inteiro rotacionar(math.Point.Inteiro p, float angulo) {
            return rotacionar(p, angulo, 0, 0);
        }

        public static math.Point.Inteiro rotacionar(math.Point.Inteiro p, float angulo, math.Point.Inteiro centro) {
            return rotacionar(p, angulo, centro.x, centro.y);
        }

        public static math.Point.Inteiro rotacionar(math.Point.Inteiro p, float angulo, int x, int y) {
            int nx = p.x;
            int ny = p.y;
            nx -= x;
            ny -= y;
            nx = (int) (Math.cos(Math.toRadians(angulo)) * nx + Math.sin(Math.toRadians(angulo)) * ny);
            ny = (int) (Math.cos(Math.toRadians(angulo)) * ny - Math.sin(Math.toRadians(angulo)) * nx);
            //Voltar à posição anterior
            nx += x;
            ny += y;
            return new math.Point.Inteiro(nx, ny);
        }
        
        public boolean equals(int x, int y, int r) {
            return Math.abs(this.x - x) < r && Math.abs(this.y - y) < r;
        }
    }
}
